/*
 * Web Crawler
 * Date: November 01, 2011
 * Group JEDi
 * Author(s): James Lovato, Efim Todorasco, Daniel Garrison
 */
package singlethread;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

/**
 * Class that takes data gathered from the DataGatherer class and displays the 
 * individual page results as well as some total aggregated data.
 * 
 * Average URLs per page is always 0.  Not sure why.
 *
 * @author James Lovato, Efim Todorasco, Daniel Garrison
 * @version 10/28/11
 * 
 */
public class Reporter {
	/**
	 * The new line constant.
	 */
	private static final String NEW_LINE = "\n";
	
	/**
	 * A constant for 3 tabs.
	 */
	private static final String TAB_3 =  "\t\t\t";
	
	/**
	 * A constant for 2 tabs.
	 */
	private static final String TAB_2 =  "\t\t";

	/**
	 * A cutoff limit for determining the correct number of tab positions based on string length.
	 */
	private static final int TAB_CUTOFF = 8;
	
	/**
	 * Constant to convert nano time to millisecond time.
	 */
	private static final int NANO_TO_MILLI = 1000000;
	
	/**
	 * Constant to convert milliseconds to seconds
	 */
	private static final int MILLI_TO_SECONDS = 1000;
	
	/**
	 * Constant to convert seconds to minutes
	 */
	private static final int SECONDS_TO_MINUTES = 60;
	
	/**
	 * The total number of pages to be crawled.
	 */
	private int my_num_of_pages;
	
	/**
	 * An array holding totals for each word count.
	 */
	private int[] my_word_count_totals;
	
	/**
	 * The total of all words counted.
	 */
	private int my_total_word_count;
	
	/**
	 * String array with all of the words to be counted.  Used for output formating.
	 */
	private String[] my_words_to_count;
	
	/**
	 * The total number of links for all pages so far. 
	 */
	private int my_total_links;
	
	/**
	 * The total amount of time spent parsing all pages so far in nanoseconds.
	 * Time to parse is time spent opening connection, creating the document, 
	 * parsing links from the document, parsing the text from the page, counting
	 * the number of "hits" and passing this information off to this Reporter.
	 */
	private long my_total_parse_time;
	
	/**
	 * A list of all outputs.  This list will be written at the end of the crawl to a 
	 * file.  This will speed the process so that one write to file can be made rather than
	 * one for each page crawled.
	 * 
	 * Note.  Speed has not been tested yet.
	 */
	//private List<String> my_list_of_outputs;
	
	private static StringBuilder my_report = new StringBuilder();
	
	DecimalFormat format = new DecimalFormat("#0.00");

	
	/**
	 * Constructor for the Reporter class.  This version is not Thread-Safe.
	 * @param the_num_of_pages
	 * @param the_words_to_count
	 */
	public Reporter(int the_num_of_pages, String[] the_words_to_count, Observer the_observer) {

		my_num_of_pages = the_num_of_pages;
		my_words_to_count = the_words_to_count;
		my_word_count_totals = new int[the_words_to_count.length];
		//my_list_of_outputs = new ArrayList<String>(the_num_of_pages);
	}
	
	/**
	 * Calling this method causes the data passed in to be printed to the console
	 * in a format as shown below.
	 * 
	 * <pre>
	 * Forms incoming data from a single report into a readable string output 
	 * and prints to the console in this form
	 * URL Parsed: http://www...
	 * Page Number: 98
	 * Word count: 13
	 * URL count: 10
	 * Average words per page: (total number of words / page number)
	 * Average URL per page: (total number of URLs / page number)
	 * Time to parse page: XXXX milliseconds
	 * Ave. page parse time: XXXX milliseconds
	 * Keyword				Total hits				Ave. Hits / Page
	 * Intelligence			6 / 128					1.31						
	 * artificial			0 / 68					0.69
	 * etc...
	 * </pre>
	 * @param the_current_url the String representing the current URL address
	 * @param the_current_page_num the current page number (Cannot be 0)
	 * @param the_word_count Array of word counts for a single parsed page
	 * @param the_time_to_parse The time it took to open a connection, create a document, parse the links and text,
	 * and pass this data in to this method.
	 * @param the_number_of_links The number of URL links for this parsed page
	 */
	public void reportPage(String the_current_url, int the_current_page_num, 
						   int[] the_word_count, long the_time_to_parse,
						   int the_number_of_links) {
		
		int word_count = 0;
		for (int i = 0; i < the_word_count.length; i++) {
			word_count += the_word_count[i];
		}
		// tally word counts
		for (int i = 0; i < the_word_count.length; i++) {
			my_word_count_totals[i] += the_word_count[i];
			my_total_word_count += the_word_count[i];
		}
		
		// tally the total number of links
		my_total_links += the_number_of_links;
		
		// tally the total parse time.
		my_total_parse_time += the_time_to_parse;
		
		// build formatted output string.
		my_report.append("URL Parsed: ");
		my_report.append(the_current_url);
		my_report.append(NEW_LINE);
		my_report.append("Page Number: ");
		my_report.append(the_current_page_num);
		my_report.append(NEW_LINE);
		my_report.append("Word count: ");
		my_report.append(word_count);
		my_report.append(NEW_LINE);
		my_report.append("URL count: ");
		my_report.append(the_number_of_links);
		my_report.append(NEW_LINE);
		my_report.append("Average words per page: ");
		my_report.append(my_total_word_count / the_current_page_num);
		my_report.append(NEW_LINE);
		my_report.append("Average URLs per page: ");
		my_report.append(my_total_links / the_current_page_num);
		my_report.append(NEW_LINE);
		my_report.append("Time to parse page: ");
		my_report.append((float) the_time_to_parse / NANO_TO_MILLI);
		my_report.append(" milliseconds");
		my_report.append(NEW_LINE);		
		my_report.append("Ave time to parse: ");
		my_report.append((float) my_total_parse_time / the_current_page_num / NANO_TO_MILLI);
		my_report.append(" milliseconds");
		my_report.append(NEW_LINE);
		my_report.append("Keyword\t\tTotal hits\tAve. hit per page");
		my_report.append(NEW_LINE);
		for (int i = 0; i < my_words_to_count.length; i++) {
			my_report.append(my_words_to_count[i]);
			my_report.append(String.format("%1$#"+ (20 - my_words_to_count[i].length())+"s", ""));
			my_report.append(the_word_count[i]);
			my_report.append(" / ");
			my_report.append(my_word_count_totals[i]);
			my_report.append("\t\t");
			my_report.append(format.format((float) my_word_count_totals[i] / the_current_page_num));
			my_report.append(NEW_LINE);
		}
		my_report.append(NEW_LINE);
		// print formatted output string to console.
		//my_list_of_outputs.add(my_report.toString());
		System.out.println(my_report.toString());
	}
	
	/**
	 * Prints a special Final report for the whole crawling process.
	 * <pre>
	 * Total number of pages crawled: 1000
	 * Total number of words: 8,000
	 * Total number of url links: 70,000
	 * Total time spent parsing: 9 minutes 39 seconds
	 * </pre>
	 * @param the_total_time the total time the application has run in milliseconds
	 */
	public void finalReport(long the_total_time, long the_total_parse_time) {
		// int seconds = (int) (the_total_time / NANO_TO_MILLI / MILLI_TO_SECONDS);
		// int minutes = seconds / SECONDS_TO_MINUTES;
		// int milliseconds = (int) (the_total_time / NANO_TO_MILLI);
		
		StringBuilder sb = new StringBuilder();
		sb.append("Total number of pages crawled: ");
		sb.append(my_num_of_pages);
		sb.append(NEW_LINE);
		sb.append("Total number of words: ");
		sb.append(my_total_word_count);
		sb.append(NEW_LINE);
		sb.append("Total number of URL links: ");
		sb.append(my_total_links);
		sb.append(NEW_LINE);
		sb.append("Total time spent running: ");
		sb.append(format.format((float) the_total_time / NANO_TO_MILLI));
		sb.append(" milliseconds");
		sb.append(NEW_LINE);
		sb.append("Total time spent parsing: ");
		sb.append(format.format((float) the_total_parse_time / NANO_TO_MILLI));
		sb.append(" milliseconds");
		sb.append(NEW_LINE);
		/*
		sb.append("Total time spend parsing: ");
		// sb.append(my_total_parse_time / NANO_TO_MILLI / MILLI_TO_SECONDS / SECONDS_TO_MINUTES);
		sb.append(minutes);
		sb.append(" minutes ");
		// sb.append(my_total_parse_time / NANO_TO_MILLI / MILLI_TO_SECONDS);
		sb.append(seconds - (minutes * SECONDS_TO_MINUTES));
		sb.append(" seconds");
		*/
		my_report.append(sb.toString());
		//System.out.println(sb.toString());
		//my_list_of_outputs.add(my_report.toString() + sb.toString());
		textWriter(sb.toString());
	}
	
	/**
	 * Writes all of the stored outputs to a text file.
	 * The current implementation uses a list of string compiled as the
	 * crawler runs and writes each String "entry" in to the file in one
	 * action so that the file is not opened an closed for each page crawled.
	 * 
	 * @param the_text
	 */
	private void textWriter(String the_output) {
		Writer output = null;
		
	    File file = new File("spiderRunST.txt");
	    
		try {
			// to append I think we need to open "spiderRunMT.txt" instead of creating a new one.
			output = new BufferedWriter(new FileWriter(file, true));
			output.append(the_output + "\n");
		    output.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println("Your file has been written");
	}
	public static String getReport(){
		return my_report.toString();
	}
	public static void reset() {
		my_report = new StringBuilder();
	}
	
}
